#pragma once
#include <functional>
#include "Log.hpp"
#include "Error.hpp"
#include "Sock.hpp"
#include <unistd.h>
#include <fcntl.h>

class httpServer;

class ThreadData
{
public:
    // ThreadData(sock, clientport, clientip, this)
    ThreadData(int sock, uint16_t port, std::string ip, httpServer *ts) :
     _sock(sock),
    _ip(ip),
    _port(port),
    thser(ts)
    {
    }

    ~ThreadData()
    {
    }

public:
    httpServer *thser;
    int _sock;
    uint16_t _port;
    std::string _ip;
};

using func_t = std::function<std::string(std::string &)>;

class httpServer
{
public:
    httpServer(func_t func, uint16_t port) : _func(func), _port(port)
    {
    }
    void initServer()
    {
        // 1.创建套接字
        _sock.Socket();
        // 2.绑定ip,port
        _sock.Bind(_port);
        // 3.监听
        _sock.Listen();
        logMessage(Info, "httpServer init...");
    }
    // 服务器不断的获取新的连接
    void startServer()
    {
        logMessage(Info, "httpServer start...");
        while (true)
        {
            std::string clientip;
            uint16_t clientport;
            int sock = _sock.Accept(&clientip, &clientport);
            if (sock < 0)
            {
                logMessage(Info, "accept failed...");
                continue;
            }
            logMessage(Info, "accept success...");
            // 使用多线程的方案，来服务一个新的连接
            // 只能使用new去创建新的对象，否则普通变量会被重复覆盖

            ThreadData *td = new ThreadData(sock, clientport, clientip, this);
            pthread_t tid;
            pthread_create(&tid, nullptr, ThreadRoutine, td);
        }
    }
    void serviceIO(int sock, const std::string &clientip, const uint16_t &clientport)
    {
        // int flags = fcntl(sock, F_GETFL, 0);
        // fcntl(sock, F_SETFL, flags | O_NONBLOCK);

        // why?
        // while (true)
        // {
            char buff[4096*4];
            std::string request;

            logMessage(Debug,"recv befroe...");
            ssize_t s = recv(sock, buff, sizeof(buff) - 1, 0);

            // ssize_t s = recv(sock, buff, sizeof(buff) - 1, MSG_DONTWAIT); //ok 循环+非阻塞
            // 这里我们就简单的处理，认为一次读取可以把完整的http报文读取上来
            if (s <= 0)
            {
                logMessage(Info, "client quit...");
                // break;
            }
            else
            {
                buff[s] = 0;
                request = buff;
                std::string response = _func(request);
                send(sock, response.c_str(), response.size(), 0);
            }
            logMessage(Debug,"send after...");

        // }
        // logMessage(Debug,"quit while...");

        // 场景如下：简单响应一个response请求，有效载荷是一个字符串
        // 可能有以下原因
        // 服务器阻塞：正如之前提到的，如果使用while循环方式去读取数据，服务器可能会阻塞在recv调用上，
        // 等待更多的数据到来。这可能导致服务器无法正确处理请求并发送响应。解决方案可以是使用非阻塞IO，
        // 或者为recv设置超时时间，避免服务器一直等待。 --- 当使用非阻塞，或者去掉循环后成功解决

        // 2.当使用一份正确的http响应代码,加上循环后，浏览器是可以正确接收到响应，并把网页展示出来！
        // 浏览器在发送HTTP请求时，会附带一些额外的请求头和信息，如User-Agent、Accept-Encoding等。
        // 而telnet客户端可能只会发送简单的请求行，而不包含这些请求头。如果服务器在处理响应时依赖于
        // 某些特定的请求头，那么没有这些请求头的请求可能会导致服务器返回不同的响应
    
 
        // 经过自己的测试，网页没有响应出hello http 两者个原因都有，所以才无法响应
        // 浏览器页面一直转圈圈是因为http服务器阻塞住了
        // 另外当浏览器输入网址后，只要存在一种资源才会发起响应，因此httpServer正确来说，只要一次IO即可
        // 即使一份很大的前端文件，我们也只能进行一次IO, http是不能也不可以进行循环的recv的！即使非阻塞也不行
        
 
    }

    static void *ThreadRoutine(void *args)
    {
        pthread_detach(pthread_self());  //不要忘记，线程与主线程分离，不然造成类似僵尸进程的问题
        ThreadData *td = static_cast<ThreadData *>(args);
        td->thser->serviceIO(td->_sock, td->_ip, td->_port);
        // delete td;  -- ？？ 你先释放了，你在close不是非法访问了吗！！！
        
        close(td->_sock);
        logMessage(Info,"sock %d,close...",td->_sock);
        delete td;
        return nullptr;
    }

    ~httpServer()
    {
    }

private:
    uint16_t _port;
    Sock _sock;
    func_t _func; // 上层业务处理的逻辑
};
